﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;

namespace PickGold
{
	/// <summary/>
	public class SocketWriter : TextWriter
	{
		private Socket _Socket;
		private EndPoint _RemotePoint;

		/// <summary/>
		public SocketWriter(Socket socket, EndPoint remotePoint)
		{
			if (socket == null)
				socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
			this._Socket = socket;
			this._RemotePoint = remotePoint;
		}

		/// <summary/>
		public Socket Client
		{
			set
			{
				this._Socket = value;
			}
			get
			{
				return this._Socket;
			}
		}

		/// <summary/>
		public EndPoint RemotePoint
		{
			set
			{
				this._RemotePoint = value;
			}
			get
			{
				return this._RemotePoint;
			}
		}

		/// <summary/>
		public override Encoding Encoding
		{
			get
			{
				return Encoding.UTF8;
			}
		}

		/// <summary>
		/// 关闭当前编写器并释放任何与该编写器关联的系统资源。
		/// </summary>
		public override void Close()
		{
			if (this._Socket == null)
				return;

			this._Socket.Close();
		}
		/// <summary>
		/// 释放由 System.IO.TextWriter 占用的非托管资源，还可以另外再释放托管资源。
		/// </summary>
		/// <param name="disposing">为 true 则释放托管资源和非托管资源；为 false 则仅释放非托管资源。</param>
		protected override void Dispose(bool disposing)
		{
			if (this._Socket == null)
				return;

			this._Socket.Close();
			this._Socket = null;
		}
		/// <summary>
		/// 清理当前编写器的所有缓冲区，使所有缓冲数据写入基础设备。
		/// </summary>
		public override void Flush()
		{
			base.Flush();
		}

		#region Write

		/// <summary>
		/// 将 Boolean 值的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 Boolean。</param>
		public override void Write(bool value)
		{
			if (this._Socket == null)
				return;

			this.Output((value ? "True" : "False").ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将字符写入文本流。
		/// </summary>
		/// <param name="value">要写入文本流中的字符。</param>
		public override void Write(char value)
		{
			if (this._Socket == null)
				return;

			this.Output(new char[] { value }, 0, 0);
		}
		/// <summary>
		/// 将字符数组写入文本流。
		/// </summary>
		/// <param name="buffer">要写入文本流中的字符数组。</param>
		public override void Write(char[] buffer)
		{
			if (this._Socket == null)
				return;

			this.Output(buffer, 0, 0);
		}
		/// <summary>
		/// 将后面带有行结束符的十进制值的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的十进制值。</param>
		public override void Write(decimal value)
		{
			if (this._Socket == null)
				return;

			this.Output(value.ToString(this.FormatProvider).ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将 8 字节浮点值的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 8 字节浮点值。</param>
		public override void Write(double value)
		{
			if (this._Socket == null)
				return;

			this.Output(value.ToString(this.FormatProvider).ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将 4 字节浮点值的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 4 字节浮点值。</param>
		public override void Write(float value)
		{
			if (this._Socket == null)
				return;

			this.Output(value.ToString(this.FormatProvider).ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将 4 字节有符号整数的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 4 字节有符号整数。</param>
		public override void Write(int value)
		{
			if (this._Socket == null)
				return;

			this.Output(value.ToString(this.FormatProvider).ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将 8 字节有符号整数的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 8 字节有符号整数。</param>
		public override void Write(long value)
		{
			if (this._Socket == null)
				return;

			this.Output(value.ToString(this.FormatProvider).ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 通过在对象上调用 ToString 将此对象的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的对象。</param>
		public override void Write(object value)
		{
			if (this._Socket == null)
				return;

			if (value == null)
				return;

			var formattable = value as IFormattable;
			if (formattable != null)
				this.Output(formattable.ToString(null, this.FormatProvider).ToCharArray(), 0, 0);
			else
				this.Output(value.ToString().ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将字符串写入文本流。
		/// </summary>
		/// <param name="value">要写入的字符串。</param>
		public override void Write(string value)
		{
			this.Output(value.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将 4 字节无符号整数的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 4 字节无符号整数。</param>
		[CLSCompliant(false)]
		public override void Write(uint value)
		{
			if (this._Socket == null)
				return;

			this.Output(value.ToString(this.FormatProvider).ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将 8 字节无符号整数的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 8 字节无符号整数。</param>
		[CLSCompliant(false)]
		public override void Write(ulong value)
		{
			if (this._Socket == null)
				return;

			this.Output(value.ToString(this.FormatProvider).ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 使用与 System.String.Format(System.String,System.Object) 相同的语义写出格式化的字符串。
		/// </summary>
		/// <param name="format">格式化字符串。</param>
		/// <param name="arg0">要写入格式化字符串的对象。</param>
		public override void Write(string format, object arg0)
		{
			if (this._Socket == null)
				return;

			if (format == null)
				this.Write(arg0);
			else
				this.Output(string.Format(format, arg0).ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 使用与 System.String.Format(System.String,System.Object) 相同的语义写出格式化的字符串。
		/// </summary>
		/// <param name="format">格式化字符串。</param>
		/// <param name="arg">要写入格式化字符串的对象数组。</param>
		public override void Write(string format, params object[] arg)
		{
			if (this._Socket == null)
				return;

			if (arg == null || arg.Length == 0)
				return;

			if (format != null)
			{
				this.Output(string.Format(format, arg).ToCharArray(), 0, 0);
				return;
			}

			if (arg.Length == 1)
			{
				this.Write(arg[0]);
				return;
			}

			var ts = new string[arg.Length];
			for (var i = 0; i < ts.Length; i++)
				ts[i] = i.ToString();
			format = "{" + string.Join("}{", ts) + "}";
			this.Output(string.Format(format, arg).ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将字符的子数组写入文本流。
		/// </summary>
		/// <param name="buffer">要从中写出数据的字符数组。</param>
		/// <param name="index">在缓冲区中开始索引。</param>
		/// <param name="count">要写入的字符数。</param>
		public override void Write(char[] buffer, int index, int count)
		{
			if (this._Socket == null)
				return;

			this.Output(buffer, index, count);
		}
		/// <summary>
		/// 使用与 System.String.Format(System.String,System.Object) 相同的语义写出格式化的字符串。
		/// </summary>
		/// <param name="format">格式化字符串。</param>
		/// <param name="arg0">要写入格式化字符串的对象。</param>
		/// <param name="arg1">要写入格式化字符串的对象。</param>
		public override void Write(string format, object arg0, object arg1)
		{
			if (this._Socket == null)
				return;

			if (format == null)
				this.Output(string.Format("{0}{1}", arg0, arg1).ToCharArray(), 0, 0);
			else
				this.Output(string.Format(format, arg0, arg1).ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 使用与 System.String.Format(System.String,System.Object) 相同的语义写出格式化的字符串。
		/// </summary>
		/// <param name="format">格式化字符串。</param>
		/// <param name="arg0">要写入格式化字符串的对象。</param>
		/// <param name="arg1">要写入格式化字符串的对象。</param>
		/// <param name="arg2">要写入格式化字符串的对象。</param>
		public override void Write(string format, object arg0, object arg1, object arg2)
		{
			if (this._Socket == null)
				return;

			if (format == null)
				this.Output(string.Format("{0}{1}{2}", arg0, arg1, arg2).ToCharArray(), 0, 0);
			else
				this.Output(string.Format(format, arg0, arg1, arg2).ToCharArray(), 0, 0);
		}

		#endregion

		#region WriteLine

		/// <summary>
		/// 将行结束符写入文本流。
		/// </summary>
		public override void WriteLine()
		{
			if (this._Socket == null)
				return;

			this.Output(this.NewLine.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将后跟行结束符的 Boolean 的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 Boolean。</param>
		public override void WriteLine(bool value)
		{
			if (this._Socket == null)
				return;

			var t = value ? "True" + this.NewLine : "False" + this.NewLine;
			this.Output(t.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将后跟行结束符的字符写入文本流。
		/// </summary>
		/// <param name="value">要写入文本流中的字符。</param>
		public override void WriteLine(char value)
		{
			if (this._Socket == null)
				return;

			var cs = new char[this.NewLine.Length + 1];
			this.NewLine.ToCharArray().CopyTo(cs, 1);
			cs[0] = value;
			this.Output(cs, 0, 0);
		}
		/// <summary>
		/// 将后跟行结束符的字符数组写入文本流。
		/// </summary>
		/// <param name="buffer">从其读取数据的字符数组。</param>
		public override void WriteLine(char[] buffer)
		{
			if (this._Socket == null)
				return;

			var cs = new char[this.NewLine.Length + buffer.Length];
			buffer.CopyTo(cs, 0);
			this.NewLine.ToCharArray().CopyTo(cs, buffer.Length);
			this.Output(cs, 0, 0);
		}
		/// <summary>
		/// 将后面带有行结束符的十进制值的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的十进制值。</param>
		public override void WriteLine(decimal value)
		{
			if (this._Socket == null)
				return;

			var t = value.ToString(this.FormatProvider) + this.NewLine;
			this.Output(t.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将后跟行结束符的 8 字节浮点值的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 8 字节浮点值。</param>
		public override void WriteLine(double value)
		{
			if (this._Socket == null)
				return;

			var t = value.ToString(this.FormatProvider) + this.NewLine;
			this.Output(t.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将后跟行结束符的 4 字节浮点值的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 4 字节浮点值。</param>
		public override void WriteLine(float value)
		{
			if (this._Socket == null)
				return;

			var t = value.ToString(this.FormatProvider) + this.NewLine;
			this.Output(t.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将后跟行结束符的 4 字节有符号整数的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 4 字节有符号整数。</param>
		public override void WriteLine(int value)
		{
			if (this._Socket == null)
				return;

			var t = value.ToString(this.FormatProvider) + this.NewLine;
			this.Output(t.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将后跟行结束符的 8 字节有符号整数的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 8 字节有符号整数。</param>
		public override void WriteLine(long value)
		{
			if (this._Socket == null)
				return;

			var t = value.ToString(this.FormatProvider) + this.NewLine;
			this.Output(t.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 通过在对象上调用 ToString 将后跟行结束符的此对象的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的对象。如果 value 为 null，则仅写入行结束字符。</param>
		public override void WriteLine(object value)
		{
			if (this._Socket == null)
				return;

			if (value == null)
				return;

			var t = this.NewLine;
			var formattable = value as IFormattable;
			if (formattable != null)
				t = formattable.ToString(null, this.FormatProvider) + t;
			else
				t = value + t;
			this.Output(t.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将后跟行结束符的字符串写入文本流。
		/// </summary>
		/// <param name="value">要写入的字符串。如果 value 为 null，则仅写入行结束字符。</param>
		public override void WriteLine(string value)
		{
			if (this._Socket == null)
				return;

			value += this.NewLine;
			this.Output(value.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将后跟行结束符的 4 字节无符号整数的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 4 字节无符号整数。</param>
		[CLSCompliant(false)]
		public override void WriteLine(uint value)
		{
			if (this._Socket == null)
				return;

			var t = value.ToString(this.FormatProvider) + this.NewLine;
			this.Output(t.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将后跟行结束符的 8 字节无符号整数的文本表示形式写入文本流。
		/// </summary>
		/// <param name="value">要写入的 8 字节无符号整数。</param>
		[CLSCompliant(false)]
		public override void WriteLine(ulong value)
		{
			if (this._Socket == null)
				return;

			var t = value.ToString(this.FormatProvider) + this.NewLine;
			this.Output(t.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 使用与 System.String.Format(System.String,System.Object) 相同的语义写出格式化的字符串和一个新行。
		/// </summary>
		/// <param name="format">格式化的字符串。</param>
		/// <param name="arg0">要写入已格式化字符串的对象。</param>
		public override void WriteLine(string format, object arg0)
		{
			if (this._Socket == null)
				return;

			if (format == null)
				this.WriteLine(arg0);
			else
				this.Write(string.Format(format, arg0) + this.NewLine);
		}
		/// <summary>
		/// 使用与 System.String.Format(System.String,System.Object) 相同的语义写出格式化的字符串和一个新行。
		/// </summary>
		/// <param name="format">格式化字符串。</param>
		/// <param name="arg">要写入格式化字符串的对象数组。</param>
		public override void WriteLine(string format, params object[] arg)
		{
			if (this._Socket == null)
				return;

			if (arg == null || arg.Length == 0)
				return;

			if (format != null)
			{
				format = string.Format(format, arg) + this.NewLine;
				this.Output(format.ToCharArray(), 0, 0);
				return;
			}

			if (arg.Length == 1)
			{
				this.WriteLine(arg[0]);
				return;
			}

			var ts = new string[arg.Length];
			for (var i = 0; i < ts.Length; i++)
				ts[i] = i.ToString();
			format = "{" + string.Join("}{", ts) + "}";
			format = string.Format(format, arg) + this.NewLine;
			this.Output(format.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 将后跟行结束符的字符子数组写入文本流。
		/// </summary>
		/// <param name="buffer">从其读取数据的字符数组。</param>
		/// <param name="index">开始读取的 buffer 中的索引。</param>
		/// <param name="count">要写入的最大字符数。</param>
		public override void WriteLine(char[] buffer, int index, int count)
		{
			var cs = buffer;
			if (count > buffer.Length / 2)
			{
				cs = new char[buffer.Length + this.NewLine.Length];
				buffer.CopyTo(cs, 0);
				this.NewLine.ToCharArray().CopyTo(cs, index + count);
				this.Output(cs, index, count + this.NewLine.Length);
				return;
			}

			cs = new char[count + this.NewLine.Length];
			for (var i = 0; i < count; i++)
				cs[i] = buffer[i + index];
			this.NewLine.ToCharArray().CopyTo(cs, count);
			this.Output(cs, index, count + this.NewLine.Length);
		}
		/// <summary>
		/// 使用与 System.String.Format(System.String,System.Object) 相同的语义写出格式化的字符串和一个新行。
		/// </summary>
		/// <param name="format">格式化字符串。</param>
		/// <param name="arg0">要写入格式字符串的对象。</param>
		/// <param name="arg1">要写入格式字符串的对象。</param>
		public override void WriteLine(string format, object arg0, object arg1)
		{
			if (this._Socket == null)
				return;

			if (format == null)
				format = string.Format("{0}{1}", arg0, arg1) + this.NewLine;
			else
				format = string.Format(format, arg0, arg1) + this.NewLine;
			this.Output(format.ToCharArray(), 0, 0);
		}
		/// <summary>
		/// 使用与 System.String.Format(System.String,System.Object) 相同的语义写出格式化的字符串和一个新行。
		/// </summary>
		/// <param name="format">格式化字符串。</param>
		/// <param name="arg0">要写入格式字符串的对象。</param>
		/// <param name="arg1">要写入格式字符串的对象。</param>
		/// <param name="arg2">要写入格式字符串的对象。</param>
		public override void WriteLine(string format, object arg0, object arg1, object arg2)
		{
			if (this._Socket == null)
				return;

			if (format == null)
				format = string.Format("{0}{1}{2}", arg0, arg1, arg2) + this.NewLine;
			else
				format = string.Format(format, arg0, arg1, arg2) + this.NewLine;
			this.Output(format.ToCharArray(), 0, 0);
		}

		#endregion

		private void Output(char[] value, int index, int count)
		{
			var buffer = this.Encoding.GetBytes(value, index, count == 0 ? value.Length : count);
			var bs = new byte[BitUtil.SIZE_GUID + BitUtil.SIZE_B64 + buffer.Length];
			Guid.NewGuid().ToByteArray().CopyTo(bs, 0);
			BitConverter.GetBytes(DateTime.UtcNow.Ticks).CopyTo(bs, BitUtil.SIZE_GUID);
			buffer.CopyTo(bs, BitUtil.SIZE_GUID + BitUtil.SIZE_B64);
			var ep = this._RemotePoint;
			if (ep == null && this._Socket.ProtocolType == ProtocolType.Udp)
			{
				ep = new IPEndPoint(IPAddress.Broadcast, ushort.MaxValue);
				this._Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
				this._RemotePoint = ep;
			}
			if (ep == null)
				this._Socket.Send(bs, SocketFlags.None);
			else
				this._Socket.SendTo(bs, SocketFlags.None, ep);
			if (this._Socket.ProtocolType != ProtocolType.Udp)
				return;

			#region udp

			this._Socket.SendTo(bs, ep);
			this._Socket.SendTo(bs, ep);
			this._Socket.SendTo(bs, ep);
			this._Socket.SendTo(bs, ep);

			#endregion
		}
	}
}
